A runtime error, also known as an exception in computer programming, is an anomalous situation that occurs during execution of a computer program. For example, in an object-oriented programming environment, an exception may occur when a particular object fails to operate in some way. Without proper handling of the exception, the computer program may produce unexpected results and, moreover, may halt execution of the program. However, a skilled computer programmer is likely to account for such an exception via code which services or handles the exception. This exception handling code essentially cleans up the memory used by an object. In this manner, the program is able to use the exception handling code to recover from the failure and continue proper execution of the program.
Runtime errors or exceptions may be handled by schemes built into the programming language itself. Using a built-in exception handling scheme, the exception is typically handled by code outside the normal flow of actual code. For example, the Visual C++ object-oriented programming language, developed and marketed by Microsoft Corporation of Redmond, Washington, provides an exemplary built-in scheme for handling runtime errors or exceptions. In Visual C++, the compiler implements exception handling using a TRY/CATCH paradigm. Essentially, a TRY statement is followed by a section of code where an exception may occur during execution of the code section. The process of raising an exception when the exception occurs in this code section is generally called "throwing" the exception. The "thrown" exception is "caught" or handled by a CATCH block of exception handling code. Within the CATCH block of code, the memory associated with an object is cleaned up to prevent a situation where the object, which is no longer in use, is left allocated from system memory. This situation is called a memory leak. Memory leaks are undesirable because they force the computer to maintain allocated system memory unnecessarily and can negatively impact performance of the executing computer program.
Built-in exception handling schemes, like the TRY/CATCH approach supported in Visual C++, are easy for computer programmers to implement. Computer programmers are able to take advantage of built-in exception handling schemes without having to spend time writing additional code to handle runtime errors or exceptions. Generally, a built-in exception handling scheme automatically cleans up the memory allocated on the program's call stack. In this manner, a built-in exception handling scheme, such as the TRY/CATCH approach supported by Visual C++, is viewed as easy to use because the built-in exception handling scheme requires no extra work for the computer programmer.
However, sometimes built-in exception handling schemes can make the compiled executable memory size of the computer program undesirably large. This is usually because the approach used by many programming languages, such as Visual C++, introduces a substantial amount of extra code when compiled into an executable form. This extra code is essentially inserted in along with the actual program code by the compiler. Compilers generally translate source code of a computer program written in a high-level language (such as Visual C++) into object code capable of being executed on the computer. When the built-in compiler exception handling scheme is turned on, the compiler translates program instructions or statements and inserts exception handling code throughout the compiled program. Using the built-in exception handling scheme of the compiler, the code handling an exception for a failed object must also appear with each instance of the object on the program's call stack. Unfortunately, this may make the size of the executable program undesirably large and waste valuable memory needed during run-time. In one example, the extra code for handling exceptions added by the compiler to the actual program code amounted to approximately 33% of the overall size of the compiled executable program.
Furthermore, this kind of built-in exception handling scheme typically requires the exception handling code to unwind the program's call stack. The program's call stack must be unwound to determine which of the objects were allocated before the exception and require cleaning up. More particularly, unwinding the program's call stack involves de-allocating or destructing all automatically allocated memory objects that were constructed since the TRY statement. As a result, unwinding the program's call stack is fairly complex and typically requires an undesirably large amount of code. Furthermore, the process of unwinding the program's call stack can be timely and memory intensive.
Memory fragmentation often may occur after a large number of objects are allocated and de-allocated. For example, a new object is typically allocated from the system memory reserved for the program, also called the heap, when a section of code needs the new object during execution. De-allocation usually occurs when an exception must be handled associated with the object or when the object is no longer used after completion of a function. The continuous allocation and de-allocation of thousands of temporary objects from the system memory can lead to an undesirable condition of memory fragmentation over time.
Typically, a process known as heap compaction can be used to merge small units of system memory into larger units of system memory. The system memory is merged into larger units so that the system memory can be used more efficiently. However, the heap compaction process must be performed off-line when the application program is not concurrently executing. System memory fragmentation is a concern when handling exceptions because handling exceptions involves cleaning up or de-allocating the system memory associated with the program. Thus, system memory fragmentation should be avoided when handling exceptions.
Other exception handling schemes use different programming constructs to avoid the need to unwind the program's call stack. For example, the Microsoft Foundation Class (MFC) 2.0 is a library of predefined classes of programming objects and macros developed and marketed by the Microsoft Corporation of Redmond, Wash. Within MFC 2.0, there is an exception handling scheme implemented using a simple SETJMP/LONGJMP mechanism. Using the SETJMP/LONGJMP mechanism, the program's call stack no longer must be unwound to handle exceptions. Instead, an exception immediately jumps to the appropriate handler for that exception. Thus, some of the memory overhead associated with using built-in schemes for exception handling can be reduced.
However, the SETJMP/LONGJMP mechanism in MFC 2.0 still requires the computer programmer to have extra code stored on the program's call stack to give the location of each allocated object. Furthermore, allocated objects are not re-used when using the SETJMP/LONGJMP mechanism to handle exceptions. All allocated objects are strictly de-allocated from system memory when they are no longer needed or when the exception handling schemes cleans up the memory (e.g., the allocated object) after an exception is thrown. Thus, system memory fragmentation can still be a problem using the SETJMP/LONGJMP mechanism from MFC 2.0.
Furthermore, the SETJMP/LONGJMP mechanism in MFC 2.0 does not prevent memory leaks. One way to prevent a memory leak when using the SETJMP/LONGJMP mechanism in MFC 2.0 is to require the programmer to write each separate exception handling cleanup code explicitly through the program. Therefore, the programmer is still required to do a great deal of work to effectively use the SETJMP/LONGJMP mechanism in MFC 2.0.
Therefore, there is a need for a system for efficiently handling an exception or runtime error while (1) avoiding unwinding of the program's call stack, (2) avoiding placing extra code on the program's call stack to handle the exception or runtime error, (3) reducing the size of the application's executable program module, (4) supporting the re-use of allocated objects, and (5) minimizing memory fragmentation, thus improving overall performance.